/*
 *  RESTful DDS is a prototyping effort to demonstrate how DDS could be combined
 *  with HTTP clients, extending the realm of pub/sub distribution to true wide
 *  area networking.
 *
 *  Copyright (C) 2009 PrismTech Ltd.
 *  reinier.torenbeek@gmail.com
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License Version 3 dated 29 June 2007, as published by the
 *  Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with restful-dds; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package track;

import java.util.Vector;
import java.util.Collection;

public final class BattleshipWrapper {
	private static String name = "";
	private static Collection<DDS.Publisher> publishers = new Vector<DDS.Publisher>();
	private static Collection<DDS.Subscriber> subscribers = new Vector<DDS.Subscriber>();

	private BattleshipWrapper() {
	}

	static {
		try {
			setName("Battleship");
		} catch (Exception ex) {
		}
	}

	@SuppressWarnings("serial")
	public static class WrapperException extends Exception {
		private final String m_reason;
		private final DDS.Entity m_entity;
		private final String m_sourceType;
		private final String m_method;
		private final int m_returnValue;
		private final String m_wrapper;

		protected WrapperException() {
			m_reason = null;
			m_entity = null;
			m_sourceType = null;
			m_method = null;
			m_returnValue = DDS.RETCODE_OK.value;
			m_wrapper = null;
		}

		public WrapperException(final String reason, final DDS.Entity entity,
				final String sourceType, final String method,
				final int returnValue, final String wrapper) {
			m_reason = reason;
			m_entity = entity;
			m_sourceType = sourceType;
			m_method = method;
			m_returnValue = returnValue;
			m_wrapper = wrapper;
		}

		public String getSourceType() {
			return m_sourceType;
		}

		public String getReason() {
			return m_reason;
		}

		public DDS.Entity getEntity() {
			return m_entity;
		}

		public String getMethod() {
			return m_method;
		}

		public int getReturnValue() {
			return m_returnValue;
		}

		public String getWrapper() {
			return m_wrapper;
		}

		@Override
		public String getMessage() {
			final StringBuilder message = new StringBuilder();

			message.append(String.format("[%s] call %s.%s failed.", m_wrapper,
					m_sourceType, m_method));

			if (m_returnValue != DDS.RETCODE_OK.value) {
				message.append(" ReturnValue: ");
				message.append(m_returnValue);
			}

			if (m_reason != null) {
				message.append(" Reason: ");
				message.append(m_reason);
			}

			return message.toString();
		}

	}

	@SuppressWarnings("serial")
	public static class ContainerWrapperException extends WrapperException {
		private final static String LINE_SEPARATOR = System
				.getProperty("line.separator"); //$NON-NLS-1$
		private final java.util.List<WrapperException> m_exceptions;

		public ContainerWrapperException(
				final java.util.List<WrapperException> exceptions) {
			m_exceptions = exceptions;
		}

		public java.util.List<WrapperException> getExceptions() {
			return java.util.Collections.unmodifiableList(m_exceptions);
		}

		@Override
		public String getMessage() {
			final StringBuilder message = new StringBuilder();

			for (WrapperException exception : m_exceptions) {
				message.append(exception.getMessage());
				message.append(LINE_SEPARATOR);
			}

			return message.toString();
		}
	}

	public synchronized static String getName() {
		return name;
	}

	/**
	 * Change the name of the Partition and update the Partition's name in the QoS 
	 * for all Publishers and Subscribers that were added with addPublisher() or addSubscriber()
	 *
	 * @throws WrapperException
	 */
	public synchronized static void setName(String partitionName)
			throws WrapperException {
		String oldName = name;
		name = partitionName;

		setPublishersPartition(oldName);
		setSubscribersPartition(oldName);
	}

	/**
	 * Add a Publisher to the Publishers list.
	 * This method does not change the QoS of the Publisher.
	 * @see #setName(String)
	 */
	public synchronized static void addPublisher(DDS.Publisher publisher) {
		if (!publishers.contains(publisher)) {
			publishers.add(publisher);
		}
	}

	/**
	 * Add a Subscriber to the Subscribers list.
	 * This method does not change the QoS of the Subscriber.
	 * @see #setName(String)
	 */
	public synchronized static void addSubscriber(DDS.Subscriber subscriber) {
		if (!subscribers.contains(subscriber)) {
			subscribers.add(subscriber);
		}
	}

	/**
	 * Remove a Publisher from the Publishers' list.
	 * This method does not change the QoS of the Publisher.
	 * @see #setName(String)
	 */
	public synchronized static void removePublisher(DDS.Publisher publisher) {
		publishers.remove(publisher);
	}

	/**
	 * Remove a Subscriber from the Subscribers' list.
	 * This method does not change the QoS of the Subscriber.
	 * @see #setName(String)
	 */
	public synchronized static void removeSubscriber(DDS.Subscriber subscriber) {
		subscribers.remove(subscriber);
	}

	private static void setSubscribersPartition(String oldName)
			throws WrapperException {
		// iterate over connected subscribers, setting partition QoS
		for (Object obj : subscribers) {
			DDS.SubscriberQos qos = new DDS.SubscriberQos();
			DDS.SubscriberQosHolder oldQos = new DDS.SubscriberQosHolder();
			DDS.Subscriber subscriber = (DDS.Subscriber) obj;

			subscriber.get_qos(oldQos);
			String[] partitions = oldQos.value.partition.name;
			Collection<String> names = new Vector<String>();

			for (int i = 0; i < partitions.length; i++) {
				names.add(partitions[i]);
			}

			if (names.contains(oldName)) {
				names.remove(oldName);
			}

			names.add(name);

			qos.partition = new DDS.PartitionQosPolicy();
			qos.partition.name = names.toArray(new String[names.size()]);

			qos.entity_factory = new DDS.EntityFactoryQosPolicy();
			qos.entity_factory.autoenable_created_entities = oldQos.value.entity_factory.autoenable_created_entities;

			int result = subscriber.set_qos(qos);

			if (result != DDS.RETCODE_OK.value) {
				throw new WrapperException(null, subscriber, "DDS.Subscriber",
						"set_qos", result,
						"track.BattleshipWrapper.setSubscribersPartition()");
			}
		}
	}

	private static void setPublishersPartition(String oldName)
			throws WrapperException {
		// iterate over connected publishers, setting partition QoS
		for (Object obj : publishers) {
			DDS.PublisherQos qos = new DDS.PublisherQos();
			DDS.PublisherQosHolder oldQos = new DDS.PublisherQosHolder();
			DDS.Publisher publisher = (DDS.Publisher) obj;

			publisher.get_qos(oldQos);
			String[] partitions = oldQos.value.partition.name;
			Collection<String> names = new Vector<String>();

			for (int i = 0; i < partitions.length; i++) {
				names.add(partitions[i]);
			}

			if (names.contains(oldName)) {
				names.remove(oldName);
			}

			names.add(name);

			qos.partition = new DDS.PartitionQosPolicy();
			qos.partition.name = names.toArray(new String[names.size()]);

			qos.entity_factory = new DDS.EntityFactoryQosPolicy();
			qos.entity_factory.autoenable_created_entities = oldQos.value.entity_factory.autoenable_created_entities;

			int result = publisher.set_qos(qos);

			if (result != DDS.RETCODE_OK.value) {
				throw new WrapperException(null, publisher, "DDS.Publisher",
						"set_qos", result,
						"track.BattleshipWrapper.setPublishersPartition()");
			}
		}
	}

}
